/*
Copyright (C) 2021-2025 Casa Xu (also Zhiyan Xu) from HIT

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

/*
 * LOS_SD.c
 *
 *  Created on: 2022年7月24日
 *      Author: 64435
 */

#include "LOS_SD.h"

uint8_t SD_Type = 0; //SD卡类型全局变量
uint32_t SD_Capacity = 0;  //SD卡容量，单位KB



/** @fn uint16_t LOS_SD_Init(void)
*   @brief SD卡初始化
*   @return 1: 初始化失败  0: 初始化成功
*   @note 该函数进行SD卡SPI模式的初始化
*/
uint16_t LOS_SD_Init(void)
{
  uint16_t i;
  uint16_t r1;
  uint16_t retry = 0;
  uint16_t retry1 = 0;
  uint16_t buf[6];

  uint16_t cmd_mid[4] = {0x00, 0x00, 0x00, 0x00}; //指令字中间四个字节

  for(i = 0; i < 9; i++)		
  {
    LOS_SD_8CLK();
    LOS_WDT_Feed();	
  }
  LOS_SD_8CLK(); //发送至少74个时钟信号（实际80个）

  //SD卡复位
  //循环发送CMD0，直到SD卡返回0x01，进入SPI模式
  retry = 0;
  do
  {
    r1 = LOS_SD_SendCMD(CMD0, cmd_mid, 0x95);//发送CMD0，CRC为0x95		
    retry++;
    if (retry % 10 == 0)
    {
      LOS_WDT_Feed();
    }
  }
  while((r1 != 0x01) && (retry < 200));//跳出循环后，检查跳出原因，
  
  if(retry == 200)	//超时，复位失败	
  {
    return 1;
  }

  //发送CMD8命令，告知SD卡主机电压信息，若有响应则版本支持
  cmd_mid[2] = 0x01;
  cmd_mid[3] = 0x44;
  r1 = LOS_SD_SendCMDNoPullCS(CMD8, cmd_mid, 0x55);
	
  if(r1 == 0x01)	
  {
    buf[0] = LOS_SD_WriteReadByte(0XFF);
    buf[1] = LOS_SD_WriteReadByte(0XFF);
    buf[2] = LOS_SD_WriteReadLastByte(0XFF); //校验位
    //多发8个时钟	  
    LOS_SD_8CLK();
    LOS_WDT_Feed();
    if (cmd_mid[3] != buf[2])  //响应不正确
    {
      return 1;
    }

    retry = 0;
    //发卡初始化指令CMD55+ACMD41，告知SD卡是否支持SDHC协议（是），使SD卡进入初始化状态
    do
    {
        cmd_mid[2] = 0x00;
        cmd_mid[3] = 0x00;
        LOS_WDT_Feed();
        do
        {
          r1 = LOS_SD_SendCMD(CMD55, cmd_mid, 0);	
          retry1++;
          if (retry1 % 10 == 0)
          {
            LOS_WDT_Feed();
          }
        }while((r1 != 0x01) && (retry1 < 200));
        LOS_WDT_Feed();
        //应返回0x01
        if(retry1 == 200)		
        {
            return 1;
        }

        retry1 = 0;

        cmd_mid[0] = 0x40;
        r1 = LOS_SD_SendCMD(ACMD41, cmd_mid, 1);	
        retry++;
        if(retry > 200)	
        {
            return 1;
        }
    }while(r1 != 0);
    //初始化指令发送完成，接下来获取OCR信息，判断SD卡版本	   
    cmd_mid[0] = 0x00;
    r1 = LOS_SD_SendCMDNoPullCS(CMD58, cmd_mid, 0);	
    //如果命令没有返回正确应答，直接退出，返回应答
    if(r1 != 0x00)  
    {
      return 1;
    }  		 
    buf[0] = LOS_SD_WriteReadByte(0XFF);
    buf[1] = LOS_SD_WriteReadByte(0XFF);
    buf[2] = LOS_SD_WriteReadByte(0XFF);
    buf[3] = LOS_SD_WriteReadLastByte(0XFF);
    //OCR接收完成，片选置高
    LOS_SD_8CLK();
    LOS_WDT_Feed();
    //检查接收到的OCR中的第30位（CCS），确定其为SD2.0还是SDHC
    //CCS=1：SDHC   CCS=0：SD2.0
    if(buf[0] & 0x40)
    {
      SD_Type = SD_TYPE_V2HC;
    }   	 
    else		
    {
      SD_Type = SD_TYPE_V2;
    }

    SD_Capacity = LOS_SD_GetCapacity();
    LOS_WDT_Feed();
  return 0; //初始化完成，返回0
 }
 else
 {
    return 1;
 }

}


/** @fn uint16_t LOS_SD_WriteReadByte(uint16_t byte)
*   @brief SD卡读写一个字节
*   @param[in] Byte      - 发送字节
*   @return MISO接收到的字节
*   @note 该函数运行完成后，片选不会拉高
*/
uint16_t LOS_SD_WriteReadByte(uint16_t byte)
{
  return LOS_SPI_WriteReadByte(LOS_SPI_SD, byte, CS_SD);
}


/** @fn uint16_t LOS_SD_WriteReadLastByte(uint16_t byte)
*   @brief SD卡读写最后一个字节
*   @param[in] Byte      - 发送字节
*   @return  MISO接收到的字节
*   @note 该函数运行完成后，片选会拉高，必须使用该函数读写最后一个字节
*/
uint16_t LOS_SD_WriteReadLastByte(uint16_t byte)
{
  return LOS_SPI_WriteReadByte(LOS_SPI_SD, byte, CS_SD);
}


/** @fn uint8_t LOS_SD_8CLK()
*   @brief SD卡发送8个空闲时钟
*   @note 该函数只会发送SPI的CLK信号，不会拉低SD卡片选
*/
uint8_t LOS_SD_8CLK()
{
  return LOS_SPI_WriteReadLastByte(LOS_SPI_SD, 0xFF, CS_NULL);
}


/** @fn uint16_t LOS_SD_SendCMD(uint16_t cmd, uint16_t* arg, uint16_t crc)
*   @brief SD卡发送CMD指令
*   @param[in] cmd   - CMD号
*   @param[in] arg   - 指令参数指针（4个字节）
*   @param[in] crc   - CRC7校验值，只有CMD0与CMD8需要使用，其余填0即可
*   @return re1 SD卡接收指令后返回的第一个字节
*   @note 该函数运行完成后，片选会拉高
*/
uint16_t LOS_SD_SendCMD(uint16_t cmd, uint16_t* arg, uint16_t crc)
{
   uint16_t re1 = 0xFF;
   uint32_t errcnt = 0;
   LOS_SD_8CLK();

   LOS_SD_WriteReadByte(cmd|0x40);
   LOS_SD_WriteReadByte(arg[0]);
   LOS_SD_WriteReadByte(arg[1]);
   LOS_SD_WriteReadByte(arg[2]);
   LOS_SD_WriteReadByte(arg[3]);
   LOS_SD_WriteReadByte(crc);
   while((re1 = LOS_SD_WriteReadByte(0xFF)) == 0xFF)
   {
      errcnt++;
      if (errcnt > 1000)
      {
        break;
      }
   }
   LOS_SD_WriteReadLastByte(0xFF);
   LOS_SD_8CLK();

   return re1;
}


/** @fn uint16_t LOS_SD_SendCMDNoPullCS(uint16_t cmd, uint16_t* arg, uint16_t crc)
*   @brief SD卡发送CMD指令（不拉高片选）
*   @param[in] cmd   - CMD号
*   @param[in] arg   - 指令参数指针（4个字节）
*   @param[in] crc   - CRC7校验值，只有CMD0与CMD8需要使用，其余填0即可
*   @return re1 SD卡接收指令后返回的第一个字节
*   @note 该函数运行完成后，片选不会拉高
*/
uint16_t LOS_SD_SendCMDNoPullCS(uint16_t cmd, uint16_t* arg, uint16_t crc) 
{
   uint16_t re1 = 0xFF;
   uint32_t errcnt = 0;

   LOS_SD_8CLK();

   LOS_SD_WriteReadByte(cmd|0x40);
   LOS_SD_WriteReadByte(arg[0]);
   LOS_SD_WriteReadByte(arg[1]);
   LOS_SD_WriteReadByte(arg[2]);
   LOS_SD_WriteReadByte(arg[3]);
   LOS_SD_WriteReadByte(crc);

   while((re1 = LOS_SD_WriteReadByte(0xFF))==0xFF)
   {
      errcnt++;
      if (errcnt > 1000)
      {
        break;
      }
   }

   LOS_SD_8CLK();
   return re1;
}



/** @fn uint8_t LOS_SD_ReceiveData(uint8_t* data, uint32_t length, uint8_t CSPULL)
*   @brief SD卡接收大量数据
*   @param[in] data     - 指向接收数据存储区的指针
*   @param[in] length   - 接收数据的长度（Byte）
*   @param[in] CSPULL   - 接收完成后是否拉高片选 0：不拉高；1：拉高
*   @return 0：接收成功 1：接收失败
*   @note 该函数是否拉高片选可通过CSPULL进行设置，此函数不能单独使用，应被读写单/多块等函数调用
*/
uint8_t LOS_SD_ReceiveData(uint8_t* data, uint32_t length, uint8_t CSPULL)
{
  uint16_t errcnt = 0;
  uint16_t r1;
  uint16_t tempdata;
  uint8_t tempdata_8bit[2];

  do
  {
    r1 = LOS_SD_WriteReadByte(0xFF);
    errcnt++;
    if (errcnt > 3000)
    {
      LOS_SD_WriteReadLastByte(0xFF);
      return 1;
    }
  } while (r1 != 0xFE);            //读数据头0xFE

  while(length--)
  {
    tempdata = LOS_SD_WriteReadByte(0XFF);
    memcpy(tempdata_8bit, &tempdata, 2);
    *data = tempdata_8bit[1];
    data++;
  }

  
  LOS_SD_WriteReadByte(0XFF);      //读第一个CRC

  if(CSPULL==0)                    //*不拉高片选
  {
    LOS_SD_WriteReadByte(0XFF);    //读第二个CRC
  }
  else                             //*拉高片选
  {
    LOS_SD_WriteReadLastByte(0XFF);//读第二个CRC
  }

  return 0;
}



/** @fn uint8_t LOS_SD_GetCID(uint8_t* cid)
*   @brief 获取SD卡CID
*   @param[in] cid     - 指向存放CID区的指针
*   @return 0：获取成功 1：获取失败
*/
uint8_t LOS_SD_GetCID(uint8_t* cid)
{
  uint16_t xyl;
  uint16_t arg[4]={0x00,0x00,0x00,0x00};
  xyl = LOS_SD_SendCMDNoPullCS(CMD10,arg,0xFF);
  if (xyl != 0x00)
  {
    return 1;
  }

  LOS_SD_ReceiveData(cid, 16, 1);
  return 0;
}


/** @fn uint8_t LOS_SD_GetCID(uint8_t* cid)
*   @brief 获取SD卡CSD
*   @param[in] csd     - 指向存放CSD区的指针
*   @return 0：获取成功 1：获取失败
*/
uint8_t LOS_SD_GetCSD(uint8_t* csd)
{
  uint16_t xyl;
  uint16_t arg[4]={0x00,0x00,0x00,0x00};
  xyl = LOS_SD_SendCMDNoPullCS(CMD9,arg,0xFF);
  if (xyl != 0x00)
  {
    return 1;
  }

  LOS_SD_ReceiveData(csd, 16, 1);
  return 0;
}


/** @fn uint8_t LOS_SD_ReadSingleBlock(uint32_t addr, uint8_t* buffer)
*   @brief SD卡读一个Block
*   @param[in] addr     - block地址
*   @param[in] buffer   - 存放读到的数据的缓冲区
*   @return 0：获取成功 1：获取失败
*/
uint8_t LOS_SD_ReadSingleBlock(uint32_t addr, uint8_t* buffer)
{
  uint8_t r1 = 0;
  uint32_t addrbuf = 0;
  uint8_t arg_8bit[4] = {0x00, 0x00, 0x00, 0x00};
  uint16_t arg[4] = {0x00, 0x00, 0x00, 0x00};
  uint32_t cnt = 0;

  if (SD_Type != SD_TYPE_V2HC)
  {
    addr = addr << 9;
  }
  
  addrbuf = addr;
  memcpy(arg_8bit, &addrbuf, 4);
  arg[0] = arg_8bit[0];
  arg[1] = arg_8bit[1];
  arg[2] = arg_8bit[2];
  arg[3] = arg_8bit[3];

  r1 = LOS_SD_SendCMDNoPullCS(CMD17,arg,0xFF);
  if (r1 != 0x00)
  {
    return 1;
  }
  r1 = LOS_SD_ReceiveData(buffer, 512, 1);
  if (r1 != 0)
  {
    return 2;
  }
  else
  {
    return 0;
  }

}


/** @fn uint8_t LOS_SD_ReadMultiBlock(uint32_t addr, uint8_t* buffer, uint16_t count)
*   @brief SD卡读多个Block
*   @param[in] addr     - block地址
*   @param[in] buffer   - 存放读到的数据的缓冲区
*   @param[in] count    - 要读取block的数量
*   @return 0：获取成功 1：获取失败
*/
uint8_t LOS_SD_ReadMultiBlock(uint32_t addr, uint8_t* buffer, uint16_t count)
{
  uint8_t r1;
  uint32_t addrbuf = 0;
  uint8_t arg_8bit[4] = {0x00, 0x00, 0x00, 0x00};
  uint16_t arg[4] = {0x00, 0x00, 0x00, 0x00};

  if(SD_Type != SD_TYPE_V2HC)
  {
    addr = addr << 9;
  }
  addrbuf = addr;
  memcpy(arg_8bit, &addrbuf, 4);
  arg[0] = arg_8bit[0];
  arg[1] = arg_8bit[1];
  arg[2] = arg_8bit[2];
  arg[3] = arg_8bit[3];
  r1 = LOS_SD_SendCMDNoPullCS(CMD18, arg, 0xFF);

  if (r1 != 0x00)
  {
    return 1;
  }

  do
  {
    if (LOS_SD_ReceiveData(buffer, 512, 0) != 0x00)
    {
      break;
    }
    buffer += 512;
  } while (--count);

  arg[0] = 0;
  arg[1] = 0;
  arg[2] = 0;
  arg[3] = 0;
  LOS_SD_SendCMD(CMD12, arg, 0xFF);
  LOS_SD_8CLK();

  if (count != 0)
  {
    return count;
  }
  else
  {
    return 0;
  }
}



/** @fn uint8_t LOS_SD_WaitReady()
*   @brief SD卡等待发送完毕
*   @note 因SD卡读写特性需要，被SD卡写多块函数调用，不单独使用
*/
uint8_t LOS_SD_WaitReady()
{
  uint8_t r1;
  uint16_t errcnt = 0;
  do
  {
    r1 = LOS_SD_WriteReadByte(0xFF);
    errcnt++;
    if (errcnt >= 0xFFFE)
    {
      return 1;
    }
  } while (r1 != 0xFF);

  return 0;
}



/** @fn uint8_t LOS_SD_WriteSingleBlock(uint32_t addr, uint8_t* data)
*   @brief SD卡写单个Block
*   @param[in] addr     - block地址
*   @param[in] buffer   - 要写入SD卡的数据缓冲区
*   @return 0：写入成功 1：写入失败
*/
uint8_t LOS_SD_WriteSingleBlock(uint32_t addr, uint8_t* data)
{
  uint8_t r1;
  uint16_t cnt;
  uint32_t addrbuf = 0;
  uint8_t arg_8bit[4] = {0x00, 0x00, 0x00, 0x00};
  uint16_t arg[4] = {0x00, 0x00, 0x00, 0x00};
  uint16_t errcnt = 0;

  if(SD_Type != SD_TYPE_V2HC)
  {
    addr = addr << 9;
  }

  addrbuf = addr;
  memcpy(arg_8bit, &addrbuf, 4);
  arg[0] = arg_8bit[0];
  arg[1] = arg_8bit[1];
  arg[2] = arg_8bit[2];
  arg[3] = arg_8bit[3];

  r1 = LOS_SD_SendCMDNoPullCS(CMD24, arg, 0x00);
  if (r1 != 0x00)
  {
    return 1;
  }

  LOS_SD_WriteReadByte(0xFF);
  LOS_SD_WriteReadByte(0xFF);
  LOS_SD_WriteReadByte(0xFF);

  LOS_SD_WriteReadByte(0xFE);  //数据开始标志

  for (cnt = 0; cnt < 512; cnt++)
  {
    LOS_SD_WriteReadByte(*data);
    data++;
  }

  LOS_SD_WriteReadByte(0xFF); //CRC
  LOS_SD_WriteReadByte(0xFF); //CRC

  r1 = LOS_SD_WriteReadByte(0xFF);
  if((r1 & 0x1F) != 0x05)     //写入失败
  {
    LOS_SD_WriteReadLastByte(0xFF);
    return 2;
  }

  while(!LOS_SD_WriteReadByte(0xFF))
  {
    errcnt++;
    if (errcnt >= 65534)
    {
      LOS_SD_WriteReadLastByte(0xFF);
      return 3;
    }
  }

  LOS_SD_WriteReadLastByte(0xFF);
  LOS_SD_8CLK();
  return 0;

}



/** @fn uint8_t LOS_SD_WriteMultiBlock(uint32_t addr, uint8_t* data, uint16_t count)
*   @brief SD卡写多个Block
*   @param[in] addr     - block地址
*   @param[in] buffer   - 要写入SD卡的数据缓冲区
*   @param[in] count    - 要写入block的数量
*   @return 0：写入成功 1：写入失败
*/
uint8_t LOS_SD_WriteMultiBlock(uint32_t addr, uint8_t* data, uint16_t count)
{
  uint8_t r1;
  uint16_t cnt;
  uint32_t addrbuf = 0;
  uint8_t arg_8bit[4] = {0x00, 0x00, 0x00, 0x00};
  uint16_t arg[4] = {0x00, 0x00, 0x00, 0x00};
  uint16_t errcnt = 0;

  if(SD_Type != SD_TYPE_V2HC)
  {
    addr = addr << 9;
  }

  addrbuf = addr;
  memcpy(arg_8bit, &addrbuf, 4);
  arg[0] = arg_8bit[0];
  arg[1] = arg_8bit[1];
  arg[2] = arg_8bit[2];
  arg[3] = arg_8bit[3];

  r1 = LOS_SD_SendCMDNoPullCS(CMD25, arg, 0x00);
  if (r1 != 0x00)
  {
    return 1;
  }

  LOS_SD_WriteReadByte(0xFF);
  LOS_SD_WriteReadByte(0xFF);
  LOS_SD_WriteReadByte(0xFF);

  do
  {
    LOS_SD_WriteReadByte(0xFC);
    for (cnt = 0; cnt < 512; cnt++)
    {
      LOS_SD_WriteReadByte(*data++);
    }

    LOS_SD_WriteReadByte(0xFF);  //CRC
    LOS_SD_WriteReadByte(0xFF);  //CRC

    r1 = LOS_SD_WriteReadByte(0xFF);
    if((r1 & 0x1F) != 0x05)     //写入失败
    {
      LOS_SD_WriteReadLastByte(0xFF);
      return 2;
    }

    if (LOS_SD_WaitReady())
    {
      LOS_SD_WriteReadLastByte(0xFF);
      return 3;
    }
  } while (--count);

  LOS_SD_WriteReadByte(0xFD);   //传输结束指令
  
  if (LOS_SD_WaitReady())
  {
    LOS_SD_WriteReadLastByte(0xFF);
    return 4;
  }

  LOS_SD_WriteReadLastByte(0xFF);
  LOS_SD_8CLK();

  return count;

}



/** @fn uint32_t LOS_SD_GetCapacity()
*   @brief SD卡获取容量信息
*   @return SD卡容量（KB）
*   @note 在SD卡初始化时调用，结果存在全局变量中，供FATFS调用
*/
uint32_t LOS_SD_GetCapacity()
{
  uint8_t csd[16];
  uint32_t Capacity;
  uint8_t r1;
  uint16_t i;
  uint16_t temp;

  if (LOS_SD_GetCSD(csd) != 0)
  {
    return 1;
  }

  if ((csd[0]&0x40)==0x40)
  {
      Capacity = (csd[9] + ((uint32_t)csd[8] << 8) + ((uint32_t)(csd[7] & 63) << 16) + 1);
      Capacity = Capacity << 9;
  }
  else		//CSD寄存器是1.0版本
  {		    
    i = csd[6]&0x03;
    i<<=8;
    i += csd[7];
    i<<=2;
    i += ((csd[8]&0xc0)>>6);
    r1 = csd[9]&0x03;
    r1<<=1;
    r1 += ((csd[10]&0x80)>>7);	 
    r1+=2;
    temp = 1;
    while(r1)
    {
      temp*=2;
      r1--;
    }
    Capacity = ((uint32_t)(i+1))*((uint32_t)temp);	 
    i = csd[5]&0x0f;
    temp = 1;
    while(i)
    {
      temp*=2;
      i--;
    }
   //最终结果
    Capacity *= (uint32_t)temp;		

  }
  return (uint32_t)Capacity;
}
